using System;
#if (!SILVERLIGHT)
using System.Data;
using System.Text;
#endif
using NUnit.Framework;
using ValidationFramework.Reflection;

namespace ValidationFramework.Tests
{

    [TestFixture]
    public class TypeDescriptorTest
    {


        [TestFixture]
        public class RuleSetClass
        {
            public class TempClass
            {

                [RangeIntRule(3, 4, RuleSet = "Car", ErrorMessage = "A car should have between 3 and 4 wheels.")]
                [RangeIntRule(2, 4, RuleSet = "Bike", ErrorMessage = "A bike should have between 2 and 4 wheels.")]
                [RangeIntRule(4, 12, RuleSet = "Truck", ErrorMessage = "A truck should have between 4 and 12 wheels.")]
                public int Property
                {
                    get;
                    set;
                }
            }
            [Test]
            public void Test()
            {
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                var propertyDescriptor = typeDescriptor.Properties["Property"];

                var carRules = propertyDescriptor.Rules.GetRulesForRuleSet("Car");
                Assert.AreEqual(1, carRules.Count);
                Assert.AreEqual("A car should have between 3 and 4 wheels.", carRules[0].ErrorMessage);

                var bikeRules = propertyDescriptor.Rules.GetRulesForRuleSet("Bike");
                Assert.AreEqual(1, bikeRules.Count);
                Assert.AreEqual("A bike should have between 2 and 4 wheels.", bikeRules[0].ErrorMessage);

                var truckRules = propertyDescriptor.Rules.GetRulesForRuleSet("Truck");
                Assert.AreEqual(1, truckRules.Count);
                Assert.AreEqual("A truck should have between 4 and 12 wheels.", truckRules[0].ErrorMessage);

            }
        }

        [TestFixture]
        public class EmptyClass
        {

            public class TempClass { }
            [Test]
            public void Test()
            {
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(0, typeDescriptor.Properties.Count);
                Assert.AreEqual(0, typeDescriptor.Fields.Count);
            }

        }

        [TestFixture]
        public class NoRulesClass
        {

            public class TempClass
            {

                public string tempField;
                public string TempProperty { get; set; }
            
            }

            [Test]
            public void Test()
            {
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(0, typeDescriptor.Properties.Count);
                Assert.AreEqual(0, typeDescriptor.Fields.Count);
            }

        }
#if (!SILVERLIGHT)
        [TestFixture]
        public class InheritFromNonIncludeAssembly
        {

			public class TempClass : DataRow
            {

                public string tempField;

				protected internal TempClass(DataRowBuilder builder) : base(builder)
				{
				}

            
            }

            [Test]
			[ExpectedException(typeof(InvalidOperationException))]
            public void TestProperty()
            {
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
            	typeDescriptor.GetOrCreatePropertyDescriptor("NonProperty");
            }
            [Test]
			[ExpectedException(typeof(InvalidOperationException))]
            public void TestField()
            {
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
            	typeDescriptor.GetOrCreateFieldDescriptor("NonField");
            }

        }
#endif
#if (!WindowsCE && !SILVERLIGHT)
        //TODO: need to put this back in
        [TestFixture]
        public class InheritFromFrameworkRulesClass
        {

            public class TempClass:ASCIIEncoding
            {

                [RequiredBoolRule]
                public override bool IsSingleByte
                {
                    get
                    {
                        return base.IsSingleByte;
                    }
                }

                public string TempProperty { get; set; }
            
            }

            [Test]
            public void Test()
            {
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(1, typeDescriptor.Properties.Count);
            }

        }

#endif
#if (!SILVERLIGHT)
        [TestFixture]
        public class AddRuleToInstanceFrameworkClass
        {

            [Test]
            public void Test()
            {
                var runtimeTypeHandle = typeof(ASCIIEncoding).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                var propertyDescriptor = typeDescriptor.GetOrCreatePropertyDescriptor("IsSingleByte");
                propertyDescriptor.Rules.Add(new RequiredBoolRule());
                Assert.AreEqual(1, typeDescriptor.Properties["IsSingleByte"].Rules.Count);
            }

        }
#endif
        [TestFixture]
        public class AddRuleToStaticFrameworkClass
        {

            [Test]
            public void Test()
            {
                var runtimeTypeHandle = typeof(DateTime).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                var propertyDescriptor = typeDescriptor.GetOrCreatePropertyDescriptor("Now");
                propertyDescriptor.Rules.Add(new RequiredRule<DateTime>());
                Assert.AreEqual(1, typeDescriptor.Properties["Now"].Rules.Count);
            }

        }

        [TestFixture]
        public class RulesClass
        {

            public class TempClass
            {

                [RequiredStringRule]
                public string tempField;
                [RequiredStringRule]
                public string TempProperty { get; set; }
           
            }

            [Test]
            public void Test()
            {
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(1, typeDescriptor.Properties.Count);
                Assert.AreEqual(1, typeDescriptor.Fields.Count);
            }

        }
        [TestFixture]
        public class RulesIncludeMemberAttributeClass
        {

            public class TempClass
            {

                [IncludeMember]
                public string tempField;
                [IncludeMember]
                public string TempProperty { get; set; }
           
            }

            [Test]
            public void Test()
            {
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(1, typeDescriptor.Properties.Count);
                Assert.AreEqual(1, typeDescriptor.Fields.Count);
            }
        }

        [TestFixture]
        public class InheritedClass
        {

            public class TempInheritedClass : TempBaseClass
            {
            }

            public class TempBaseClass
            {

                [IncludeMember]
                public string tempField;
                [IncludeMember]
                public string TempProperty { get; set; }
         
            }

            [Test]
            public void Test()
            {
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempInheritedClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(1, typeDescriptor.Properties.Count);
                Assert.AreEqual(1, typeDescriptor.Fields.Count);
            }

        }

        [TestFixture]
        public class GetOrCreatePropertyDescriptorNoRules
        {

            public class TempInheritedClass : TempBaseClass
            {
            }

            public class TempBaseClass
            {
                public string tempField;
                public string TempProperty { get; set; }
            }

            [Test]
            public void Named()
            {
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempInheritedClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(0, typeDescriptor.Properties.Count);
                Assert.AreEqual(0, typeDescriptor.Fields.Count);
                var propertyDescriptor = typeDescriptor.GetOrCreatePropertyDescriptor("TempProperty");
                var fieldDescriptor = typeDescriptor.GetOrCreateFieldDescriptor("tempField");
                Assert.IsNotNull(propertyDescriptor);
                Assert.IsNotNull(fieldDescriptor);
                Assert.AreEqual(1, typeDescriptor.Properties.Count);
                Assert.AreEqual(1, typeDescriptor.Fields.Count);
            }

#if (!WindowsCE)

            [Test]
            public void Lambda()
            {
                TypeCache.Clear();
                var tempInheritedClass = new TempInheritedClass();
                var runtimeTypeHandle = typeof(TempInheritedClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(0, typeDescriptor.Properties.Count);
                Assert.AreEqual(0, typeDescriptor.Fields.Count);
                var propertyDescriptor = typeDescriptor.GetOrCreatePropertyDescriptor(() => tempInheritedClass.TempProperty);
                var fieldDescriptor = typeDescriptor.GetOrCreateFieldDescriptor(() => tempInheritedClass.tempField);
                Assert.IsNotNull(propertyDescriptor);
                Assert.IsNotNull(fieldDescriptor);
                Assert.AreEqual(1, typeDescriptor.Properties.Count);
                Assert.AreEqual(1, typeDescriptor.Fields.Count);
            }
#endif
        }
        [TestFixture]
        public class InheritedWithStaticOnBaseClass
        {

            public class TempInheritedClass : TempBaseClass
            {
            }

            public class TempBaseClass
            {
          
                [IncludeMember]
                public static string TempProperty { get; set; }
          
            }

            [Test]
            public void Test()
            {
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempInheritedClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(0, typeDescriptor.Properties.Count);
            }

        }

        [TestFixture]
        public class InheritedWithStaticOnBaseAndInstanceOnInheritedClass
        {

            public class TempInheritedClass : TempBaseClass
            {

                [IncludeMember]
                public new string TempProperty { get; set; }
           
            }

            public class TempBaseClass
            {
               
                [IncludeMember]
                public static string TempProperty { get; set; }
           
            }

            [Test]
            public void Test()
            {
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempInheritedClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(1, typeDescriptor.Properties.Count);
            }

        }

        [TestFixture]
        public class InheritedWithStaticMembersClass
        {

            public class TempInheritedClass : TempBaseClass
            {
            }

            public class TempBaseClass
            {
              
                [IncludeMember]
                public static string tempField;
                [IncludeMember]
                public static string TempProperty { get; set; }
          
            }

            [Test]
            public void Test()
            {
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempInheritedClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(0, typeDescriptor.Properties.Count);
                Assert.AreEqual(0, typeDescriptor.Fields.Count);
            }

        }

        [TestFixture]
        public class InheritedWithOverrideVirtualPropertyClass
        {

            public class TempInheritedClass : TempBaseClass
            {
              
                [LengthStringRule(2)]
                public override string TempProperty { get; set; }

            }

            public class TempBaseClass
            {
              
                [RequiredStringRule]
                public virtual string TempProperty { get; set; }
        
            }

            [Test]
            public void Test()
            {
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempInheritedClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(1, typeDescriptor.Properties.Count);
                Assert.AreEqual(2, typeDescriptor.Properties["TempProperty"].Rules.Count);
            }

        }
        [TestFixture]
        public class InheritedWithNewPropertyClass
        {

            public class TempInheritedClass : TempBaseClass
            {
             
                [LengthStringRule(2)]
                public new virtual string TempProperty { get; set; }
           
            }

            public class TempBaseClass
            {
          
                [RequiredStringRule]
                public virtual string TempProperty { get; set; }
         
            }

            [Test]
            public void Test()
            {
                //TODO: not sure if this is the correct behaviour??
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempInheritedClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(1, typeDescriptor.Properties.Count);
                Assert.AreEqual(1, typeDescriptor.Properties["TempProperty"].Rules.Count);
            }

        }


        [TestFixture]
        public class RealyComplexClass
        {

			public class BottomClass : TempClass
			{

			}
			public class TempClass : BaseClass, TempInterface1, TempInterface2
			{

				[RequiredIntRule(InitialValue = 3)]
				int TempInterface1.TempProperty
				{
					get;
					set;
				}

				[RequiredIntRule(InitialValue = 6)]
				int TempInterface2.TempProperty
				{
					get;
					set;
				}

				[RequiredIntRule(InitialValue = 5)]
				public override int TempProperty { get; set; }				

				
			}

			public abstract class BaseClass:TempInterface1
			{
				[RequiredIntRule(InitialValue = 4)]
				int TempInterface1.TempProperty { get; set; }

				[RequiredIntRule(InitialValue = 3)]
				public virtual int TempProperty{get;set;}
			}

            public interface TempInterface1
            {
                [RequiredIntRule]
                int TempProperty
                {
                    get;
                    set;
                }
            }

            public interface TempInterface2
            {
                [RequiredIntRule(InitialValue = 7)]
                int TempProperty
                {
                    get;
                    set;
                }
            }

            [Test]
            public void Test()
            {
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(3, typeDescriptor.Properties.Count);
				Assert.AreEqual(3, typeDescriptor.Properties["ValidationFramework.Tests.TypeDescriptorTest.RealyComplexClass.TempInterface1.TempProperty"].Rules.Count);
				Assert.AreEqual(2, typeDescriptor.Properties["ValidationFramework.Tests.TypeDescriptorTest.RealyComplexClass.TempInterface2.TempProperty"].Rules.Count);
				Assert.AreEqual(2, typeDescriptor.Properties["TempProperty"].Rules.Count);

                var bottomRuntimeTypeHandle = typeof(BottomClass).TypeHandle;
                var bottomTypeDescriptor = new TypeDescriptor(bottomRuntimeTypeHandle);
                Assert.AreEqual(bottomRuntimeTypeHandle, bottomTypeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(3, bottomTypeDescriptor.Properties.Count);
				Assert.AreEqual(3, bottomTypeDescriptor.Properties["ValidationFramework.Tests.TypeDescriptorTest.RealyComplexClass.TempInterface1.TempProperty"].Rules.Count);
				Assert.AreEqual(2, bottomTypeDescriptor.Properties["ValidationFramework.Tests.TypeDescriptorTest.RealyComplexClass.TempInterface2.TempProperty"].Rules.Count);
				Assert.AreEqual(2, bottomTypeDescriptor.Properties["TempProperty"].Rules.Count);
            }

        }
        [TestFixture]
        public class ExplicitBypassBaseClass
        {

			public class TempClass : BaseClass, TempInterface2
			{

				[RequiredIntRule(InitialValue = 6)]
				int TempInterface2.TempProperty
				{
					get;
					set;
				}

				[RequiredIntRule(InitialValue = 5)]
				public override int TempProperty { get; set; }				

				
			}

			public abstract class BaseClass
			{

				[RequiredIntRule(InitialValue = 3)]
				public virtual int TempProperty{get;set;}
			}

         

            public interface TempInterface2
            {
                [RequiredIntRule(InitialValue = 7)]
                int TempProperty
                {
                    get;
                    set;
                }
            }

            [Test]
            public void Test()
            {
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(2, typeDescriptor.Properties.Count);
				Assert.AreEqual(2, typeDescriptor.Properties["ValidationFramework.Tests.TypeDescriptorTest.ExplicitBypassBaseClass.TempInterface2.TempProperty"].Rules.Count);
				Assert.AreEqual(2, typeDescriptor.Properties["TempProperty"].Rules.Count);
            }

        }


        [TestFixture]
        public class ExplicitInterfaceClass
        {

			public class TempClass : TempInterface
			{

				[RequiredIntRule(InitialValue = 3)]
				int TempInterface.TempProperty
				{
					get;
					set;
				}

				[RequiredIntRule(InitialValue = 3)]
				public int TempProperty
				{
					get;
					set;
				}
			}

            public interface TempInterface
            {
                [RequiredIntRule]
                int TempProperty
                {
                    get;
                    set;
                }
            }

            [Test]
            public void Test()
            {
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(2, typeDescriptor.Properties.Count);
				Assert.AreEqual(2, typeDescriptor.Properties["ValidationFramework.Tests.TypeDescriptorTest.ExplicitInterfaceClass.TempInterface.TempProperty"].Rules.Count);
				Assert.AreEqual(1, typeDescriptor.Properties["TempProperty"].Rules.Count);
            }

        }

        [TestFixture]
        public class InheritedWithOverrideAbstractPropertyClass
        {
            public class TempInheritedClass : TempBaseClass
            {
                [LengthStringRule(2)]
                public override string TempProperty { get; set; }

            }

            public abstract class TempBaseClass
            {
                [RequiredStringRule]
                public abstract string TempProperty { get; set; }
            }

            [Test]
            public void Test()
            {
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempInheritedClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(1, typeDescriptor.Properties.Count);
                Assert.AreEqual(2, typeDescriptor.Properties["TempProperty"].Rules.Count);
            }

        }

        [TestFixture]
        public class ImplementInterfaceClass
        {

            public class TempClass : TempInterface
            {
                [LengthStringRule(2)]
                public string TempProperty { get; set; }
            }

            public interface TempInterface
            {
                [RequiredStringRule]
                string TempProperty { get; set; }
            }

            [Test]
            public void Test()
            {
                TypeCache.Clear();
                var runtimeTypeHandle = typeof(TempClass).TypeHandle;
                var typeDescriptor = new TypeDescriptor(runtimeTypeHandle);
                Assert.AreEqual(runtimeTypeHandle, typeDescriptor.RuntimeTypeHandle);
                Assert.AreEqual(1, typeDescriptor.Properties.Count);
                Assert.AreEqual(2, typeDescriptor.Properties["TempProperty"].Rules.Count);
            }

        }

        [TestFixture]
        public class PropertyWithNoGetClass
        {

            public class TempClass 
            {
                public string TempProperty
                {
                    set {  }
                }
            }


            [Test]
            [ExpectedException(typeof(ArgumentException))]
            public void Test()
            {
                var typeDescriptor = TypeCache.GetType(typeof(TempClass).TypeHandle);
                typeDescriptor.GetOrCreatePropertyDescriptor("TempProperty");
            }

        }

        [TestFixture]
        public class Construction
        {

            [Test]
            [ExpectedException(typeof(ArgumentException))]
            public void ExceptionInterface()
            {
                var runtimeTypeHandle = typeof(IDisposable).TypeHandle;
                new TypeDescriptor(runtimeTypeHandle);
            }

        }
    }
}